﻿using System;
using System.Globalization;
using System.Windows;

namespace RG.Common.Export
{

    /// <summary>
    /// Константы для автоматического определения ширины столбца
    /// </summary>
    public static class ColumnGridWidthType
    {
        public const int Star = -1;
        public const int Auto = -2;
    }

    public interface IColumnInfo
    {
        /// <summary>
        /// Нужен чтобы ловились PropertyChanged и грид перерисовывался
        /// </summary>
        string PropertyPath { get; }

        /// <summary>
        /// Заголовок колонки
        /// </summary>
        object Header { get; }

        /// <summary>
        /// Имя шаблона для заголовка
        /// </summary>
        string HeaderTemplateName { get; }

        /// <summary>
        /// Ширина колонки по умолчанию
        /// </summary>
        int Width { get; }

        /// <summary>
        /// Тиа данных для Excel
        /// </summary>
        /// <returns></returns>
        string ExcelDataType();

        /// <summary>
        /// Получает значение колонки для объекта item в формате для Excel
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        string GetValueForExcel(object item);

        /// <summary>
        /// Получает значение колонки для объекта item в формате для отображения в гриде
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        string GetValueForGrid(object item);

        /// <summary>
        /// Задает имя шаблона для ячейки
        /// </summary>
        string TemplateName { get; }

        /// <summary>
        /// Возвращает значения колонки для объекта item для сортировки
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        IComparable GetValueForCompare(object item);
    }

    public static class ColumnInfoTemplates
    {
        public const string String = "string";
        public const string StringWrap = "stringwrap"; // строка с переносом
        public const string Int = "int";
        public const string Long = "long";
        public const string Number = "num";
        public const string Date = "date";
        public const string Bool = "bool";

        public static bool IsStandart(string templateName)
        {
            return String.IsNullOrEmpty(templateName) ||
                   templateName == String || templateName == Int || templateName == Long ||
                   templateName == Number || templateName == Date || templateName == Bool || templateName == StringWrap;
        }
    }

    public abstract class ColumnInfo<TItem, TValue, TCompared, TDContext> : IColumnInfo
    {
        protected ColumnInfo()
        {
        }

        protected ColumnInfo(TDContext header, Func<TItem, TValue> getValueFunc)
        {
            Header = header;
            GetValueFunc = getValueFunc;
            Width = 70;
        }

        protected ColumnInfo(TDContext header, int width, Func<TItem, TValue> getValueFunc) :
            this(header, getValueFunc)
        {
            Width = width;
        }

        protected ColumnInfo(string propertyPath, TDContext header, int width, Func<TItem, TValue> getValueFunc) : 
            this(header, width, getValueFunc)
        {
            PropertyPath = propertyPath;
        }

        protected ColumnInfo(TDContext header, int width, Func<TItem, TValue> getValueFunc, Func<TItem, TCompared> compareFunc) :
            this(header, width, getValueFunc)
        {
            CompareFunc = compareFunc;
        }

        protected ColumnInfo(string propertyPath, TDContext header, int width, Func<TItem, TValue> getValueFunc, Func<TItem, TCompared> compareFunc) :
            this(header, width, getValueFunc, compareFunc)
        {
            PropertyPath = propertyPath;
        }

        public string PropertyPath { get; set; }
        public object Header { get; protected set; }

        public int Width { get; protected set; }

        public Func<TItem, TValue> GetValueFunc { get; private set; }
        public Func<TItem, TCompared> CompareFunc { get; private set; }

        protected TItem GetItem(object item)
        {
            return (item is TItem) ? (TItem)item : default(TItem);
        }
        
        protected TValue GetValue(object item)
        {
                return (item is TItem) ? GetValueFunc((TItem)item) : default(TValue);
            }

        public virtual string ExcelDataType()
        {
            return "string";
        }

        public virtual string GetValueForExcel(object item)
        {
            return GetValueToString(item);
        }

        private string GetValueToString(object item)
        {
            var val = GetValue(item);
// ReSharper disable CompareNonConstrainedGenericWithNull
            return val == null ? null : val.ToString();
// ReSharper restore CompareNonConstrainedGenericWithNull
        }

        public virtual string GetValueForGrid(object item)
        {
            return GetValueToString(item);
        }

        public string TemplateName { get; set; }

        public string HeaderTemplateName { get; set; }

        public virtual IComparable GetValueForCompare(object item)
        {
            if (CompareFunc != null)
                return (IComparable)CompareFunc(GetItem(item));
            var v = GetValue(item);
            // чтобы могли сортироваться сгруппированные по int гриды (v в этом случае null выходит и всё падает)
            // хотя всё равно криво немного сортирует
            if (v == null || item is int) return "!";
            return v is IComparable ? (IComparable) v : v.ToString();
        }
    }

    public class ColumnInfoObj<T> : ColumnInfo<T, String, String, object>
    {
        public ColumnInfoObj(object header, Func<T, String> getValueFunc)
            : base(header, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.String;
            HeaderTemplateName = ColumnInfoTemplates.String;
        }

        public ColumnInfoObj(object header, int width, Func<T, String> getValueFunc)
            : base(header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.String;
            HeaderTemplateName = ColumnInfoTemplates.String;
        }

        public ColumnInfoObj(string propertyName, object header, int width, Func<T, String> getValueFunc)
            : base(propertyName, header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.String;
            HeaderTemplateName = ColumnInfoTemplates.String;
        }
    }

    public class ColumnInfoStringWrap<T> : ColumnInfo<T, String, String, String>
    {
        public ColumnInfoStringWrap(string header, Func<T, String> getValueFunc)
            : base(header, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.StringWrap;
        }

        public ColumnInfoStringWrap(string header, int width, Func<T, String> getValueFunc)
            : base(header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.StringWrap;
        }

        public ColumnInfoStringWrap(string propertyName, string header, int width, Func<T, String> getValueFunc)
            : base(propertyName, header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.StringWrap;
        }
    }

    public class ColumnInfoString<T, TCompared, TDContext> : ColumnInfo<T, String, TCompared, TDContext>
    {
        public ColumnInfoString(TDContext header, Func<T, String> getValueFunc)
            : base(header, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.String;
        }

        public ColumnInfoString(TDContext header, int width, Func<T, String> getValueFunc)
            : base(header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.String;
        }

        public ColumnInfoString(string propertyName, TDContext header, int width, Func<T, String> getValueFunc)
            : base(propertyName, header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.String;
        }
    }

    public class ColumnInfoString<T, TCompare> : ColumnInfo<T, String, TCompare, string>
    {
        public ColumnInfoString(string header, Func<T, String> getValueFunc)
            : base(header, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.String;
        }

        public ColumnInfoString(string header, int width, Func<T, String> getValueFunc, Func<T, TCompare> compareFunc)
            : base(header, width, getValueFunc, compareFunc)
        {
            TemplateName = ColumnInfoTemplates.String;
        }
    }

    public class ColumnInfoString<T> : ColumnInfo<T, String, string, string>
    {
        public ColumnInfoString(string header, Func<T, String> getValueFunc)
            : base(header, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.String;
        }

        public ColumnInfoString(string header, int width, Func<T, String> getValueFunc)
            : base(header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.String;
        }

        public ColumnInfoString(string propertyName, string header, int width, Func<T, String> getValueFunc)
            : base(propertyName, header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.String;
        }
    }

    public class ColumnInfoInt<T, TDContext> : ColumnInfo<T, int?, int?, TDContext>
    {
        public ColumnInfoInt(TDContext header, Func<T, int?> getValueFunc)
            : base(header, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Long;
        }

        public ColumnInfoInt(TDContext header, int width, Func<T, int?> getValueFunc)
            : base(header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Long;
        }

        public override string ExcelDataType()
        {
            return "int";
        }

        public override string GetValueForExcel(object item)
        {
            var val = GetValue(item);
            return val.HasValue ? val.Value.ToString() : "";
        }

        public override string GetValueForGrid(object item)
        {
            var val = GetValue(item);
            return val.HasValue ? val.Value.ToString() : "";
        }

        public override IComparable GetValueForCompare(object item)
        {
            return GetValue(item);
        }
    }

    public class ColumnInfoInt<T> : ColumnInfoInt<T, string>
    {
        public ColumnInfoInt(string header, Func<T, int?> getValueFunc)
            : base(header, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Long;
        }

        public ColumnInfoInt(string header, int width, Func<T, int?> getValueFunc)
            : base(header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Long;
        }
    }

    public class ColumnInfoLong<T> : ColumnInfo<T, long?, long?, string >
    {
        public ColumnInfoLong(string header, Func<T, long?> getValueFunc)
            : base(header, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Int;
        }

        public ColumnInfoLong(string header, int width, Func<T, long?> getValueFunc)
            : base(header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Int;
        }

        public override string ExcelDataType()
        {
            return "long";
        }

        public override string GetValueForExcel(object item)
        {
            var val = GetValue(item);
            return val.HasValue ? val.Value.ToString() : "";
        }

        public override string GetValueForGrid(object item)
        {
            var val = GetValue(item);
            return val.HasValue ? val.Value.ToString() : "";
        }

        public override IComparable GetValueForCompare(object item)
        {
            return GetValue(item);
        }
    }

    public class ColumnInfoBool<T> : ColumnInfo<T, bool?, bool?, string>
    {
        public ColumnInfoBool(string header, Func<T, bool?> getValueFunc)
            : base(header, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Bool;
        }

        public ColumnInfoBool(string header, int width, Func<T, bool?> getValueFunc)
            : base(header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Bool;
        }

        public ColumnInfoBool(string propertyPath, string header, int width, Func<T, bool?> getValueFunc)
            : base(propertyPath, header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Bool;
        }

        public override string ExcelDataType()
        {
            return "bool";
        }

        public override string GetValueForExcel(object item)
        {
            var val = GetValue(item);
            return val.HasValue ? (val.Value ? "Да" : "Нет") : "";
        }

        public override string GetValueForGrid(object item)
        {
            var val = GetValue(item);
            return val.HasValue ? val.Value.ToString() : "";
        }

        public override IComparable GetValueForCompare(object item)
        {
            return GetValue(item);
        }
    }

    public class ColumnInfoNum<T> : ColumnInfo<T, decimal?, decimal?, string>
    {
        public ColumnInfoNum(string header, Func<T, decimal?> getValueFunc): base(header, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Number;
        }

        public ColumnInfoNum(string header, int width, Func<T, decimal?> getValueFunc)
            : base(header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Number;
        }

        public ColumnInfoNum(string propertyName, string header, int width, Func<T, decimal?> getValueFunc)
            : base(propertyName, header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Number;
        }

        public override string ExcelDataType()
        {
            return "number";
        }

        public override string GetValueForExcel(object item)
        {
            var val = GetValue(item);
            return val.HasValue ? val.Value.ToString(CultureInfo.InvariantCulture) : "";
        }

        public override string GetValueForGrid(object item)
        {
            var val = GetValue(item);
            return val.HasValue ? val.Value.ToString(NumericFormat, FormatCulture) : string.Empty;
        }

        public override IComparable GetValueForCompare(object item)
        {
            return GetValue(item);
        }

        public string NumericFormat = "N2";
        static readonly CultureInfo FormatCulture = CultureInfo.GetCultureInfo("ru-RU");
    }

    public class ColumnInfoDate<T> : ColumnInfo<T, DateTime?, decimal?, string>
    {
        public ColumnInfoDate(string header, Func<T, DateTime?> getValueFunc): base(header, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Date; 
            ExcelType = "date";
            ExcelFormat = "s";
            DisplayFormat = "d";
        }

        public ColumnInfoDate(string header, int width, Func<T, DateTime?> getValueFunc)
            : base(header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Date;
            ExcelType = "date";
            ExcelFormat = "s";
            DisplayFormat = "d";
        }

        public ColumnInfoDate(string header, int width, Func<T, DateTime?> getValueFunc, string displayFormat, string excelFormat)
            : base(header, width, getValueFunc)
        {
            TemplateName = ColumnInfoTemplates.Date;
            // ToDo ROY сделать правильно
            ExcelType = displayFormat == "dd.MM.yyyy HH:mm:ss" ? "string" : "date";
            ExcelFormat = excelFormat;
            DisplayFormat = displayFormat;
        }

        public override string ExcelDataType()
        {
            return ExcelType;
        }

        public override string GetValueForExcel(object item)
        {
            var value = GetValue(item);
            string temp = value.HasValue && value > new DateTime(1900, 01, 01) ? value.Value.ToString(ExcelFormat, CultureInfo.GetCultureInfo("ru-RU")) : "";
            return temp;
        }

        public override string GetValueForGrid(object item)
        {
            var value = GetValue(item);
            return value.HasValue ? value.Value.ToString(DisplayFormat, CultureInfo.GetCultureInfo("ru-RU")) : "";
        }

        public override IComparable GetValueForCompare(object item)
        {
            return GetValue(item);
        }

        private string DisplayFormat { get; set; }
        private string ExcelFormat { get; set; }
        private string ExcelType { get; set; }
    }

    // класс для сохранение сеттингов по ДатаГриду
    public class ColumnPreset
    {
        public string Header { get; set; }
        public string Comment { get; set; }
        public decimal Width { get; set; }
        public int DisplayPosition { get; set; }
        public Visibility Visible { get; set; }
    }
}
